 /**************************************************************
 *  Filename:    Modbus_master.cpp
 *  Copyright:   西安爱极客，Xi'an Aijike Network Technology Co., Ltd
 *  Description: 1.生成modbus指令数组
 *               2.解析modbus响应
 *  @author:     孙毅明
 *  @version     2022.10.06 
 **************************************************************/
#include "Modbus_master.h"
//响应成功
uint8_t ModbusMasterResponseOk  = 0;
//定义接收数组指针
uint8_t ModbusMasterReceBuf[1024] ={0};
//定义发送数组指针
uint8_t ModbusMasterSendBuf[1024] ={0};
//定义接收长度指针
uint16_t *ModbusMasterReceLength ;
//从站站号
uint8_t ModbusMaster_SlaveNum = 1;
//临时寄存器，用于缓存读到的数据
uint8_t ModbusMasterTempBuf[32] = {0};
//读超时时间
uint16_t ModbusMasterWriteTimeOut = 3000;//ms
//写超时时间
uint16_t ModbusMasterReadTimeOut = 3000;//ms
//写超时时间
uint16_t ModbusMasterRetry = 1;//ms

//读保持寄存器0x03
// return         返回待发送数据长度
uint16_t ReadHoldRegister(uint16_t RegAddress, uint16_t num)
{
    if (num > 32)
        return 0;
    ModbusMasterSendBuf[0] = ModbusMaster_SlaveNum;       //站号
    ModbusMasterSendBuf[1] = 0x03;                        //功能码
    ModbusMasterSendBuf[2] = RegAddress >> 8;             //起始地址 H
    ModbusMasterSendBuf[3] = RegAddress;                  //起始地址 L
    ModbusMasterSendBuf[4] = num >> 8;                    //数据长度 H
    ModbusMasterSendBuf[5] = num;                         //数据长度 L
    uint16_t crc = MasterCalcCrc(ModbusMasterSendBuf, 6); // CRC校验
    ModbusMasterSendBuf[6] = crc;
    ModbusMasterSendBuf[7] = crc >> 8;
    return 8;
}

// //写单个寄存器0x06
// uint16_t WriteOneHoldRegister(uint16_t RegAddress,uint16_t data)
// {
//     ModbusMasterResponseOk = 0 ;
//     ModbusMasterSendBuf[0] = ModbusMaster_SlaveNum;//站号
//     ModbusMasterSendBuf[1] = 0x06;//功能码
//     ModbusMasterSendBuf[2] = RegAddress>>8;//起始地址 H
//     ModbusMasterSendBuf[3] = RegAddress;   //起始地址 L
//     ModbusMasterSendBuf[4] = data>>8;//数据长H
//     ModbusMasterSendBuf[5] = data;   //数据 L
//     uint16_t crc = MasterCalcCrc(ModbusMasterSendBuf, 6);//CRC校验
//     ModbusMasterSendBuf[6] = crc;
//     ModbusMasterSendBuf[7] = crc>>8;
//     //发送
//     // Uart5_Start_DMA_Tx(8);
//     uint16_t cout = ModbusMasterWriteTimeOut/10;
//     //等待反馈
//     while(ModbusMasterResponseOk != 1 && cout !=0)
//     {
//         cout--;
//         // osdelay_ms(10);
//     }
//     return ModbusMasterResponseOk;
// }
//写多个寄存器0x10
// uint16_t RegAddress 寄存器起始地址
// uint16_t* buf       数据指针
// uint16_t num        寄存器个数
// return         返回待发送数据长度
uint16_t WriteMultipleHoldRegister(uint16_t RegAddress, uint16_t *dataBuf, uint16_t num)
{
    uint16_t len = 0;
    if (num > 32)
        return 0;
    ModbusMasterSendBuf[0] = ModbusMaster_SlaveNum; //站号
    ModbusMasterSendBuf[1] = 0x10;                  //功能码
    ModbusMasterSendBuf[2] = RegAddress >> 8;       //起始地址 H
    ModbusMasterSendBuf[3] = RegAddress;            //起始地址 L
    ModbusMasterSendBuf[4] = num >> 8;              //数据长度 H
    ModbusMasterSendBuf[5] = num;                   //数据长度 L
    ModbusMasterSendBuf[6] = num * 2;               //字节数
    for (uint8_t i = 0; i < num; i++)
    {
        ModbusMasterSendBuf[7 + 2 * i] = dataBuf[i] >> 8;
        ModbusMasterSendBuf[7 + 2 * i + 1] = dataBuf[i];
    }
    uint16_t crc = MasterCalcCrc(ModbusMasterSendBuf, 7 + num * 2); // CRC校验
    ModbusMasterSendBuf[7 + num * 2] = crc;
    ModbusMasterSendBuf[7 + num * 2 + 1] = crc >> 8;
    len = 7 + num * 2 + 1 + 1;
    //返回数据长度
    return len;
}

void AnalyzeResponse()//分析从站响应报文
{
    uint16_t _crc;
    uint8_t a1, a2;
    _crc = MasterCalcCrc(ModbusMasterReceBuf, (*ModbusMasterReceLength - 2)); //计算校验CRC
    a1 = _crc & 0xff;                      //CRC低字节
    a2 = (_crc >> 8) & 0xff;               //CRC高字节
    if(a1 == ModbusMasterReceBuf[*ModbusMasterReceLength - 2] && a2 == ModbusMasterReceBuf[*ModbusMasterReceLength - 1]) //校验正确
    {
        switch(ModbusMasterReceBuf[1])
        {
        case 0x03:
            Response_03();
            break;	                   //读寄存器
        case 0x06:
            Response_06();
            break;                    //写单个寄存器
        case 0x10:
            Response_10();
            break;                    //写多个寄存器
        default:
            break;           //不支持的功能码
        }
    }
}


/*分析03功能码报文，读寄存器*/
void Response_03()
{
    for(uint8_t i = 0; i< ModbusMasterReceBuf[2]; i+=2)
    {
        ModbusMasterTempBuf[i/2] = (ModbusMasterReceBuf[3+i]	<< 8)| ModbusMasterReceBuf[4+i];
    }
    ModbusMasterResponseOk = 1;
}


/*分析06功能码报文，写单个寄存器*/
void Response_06()
{
    ModbusMasterResponseOk = 1;
}

/*分析10功能码报文，写多个寄存器*/
void Response_10()//接收到数据
{
    ModbusMasterResponseOk = 1;
}

/*************************************************
crc16校验码计算函数,计算算法：
1、设置crc校验寄存器的初值为0xffff;
2、将报文的第一个字节与crc寄存器的低字节异或，结果存入crc寄存器
3、判断crc的第一位是否为1，如果是1,crc右移1位后和0xa001异或，如果为0，则再移1位；
4、重复步骤3，直到完成8个位；
5、重复步骤2、3、4直到完成所有字节
6、返回计算结果
***********************************************/
uint16_t MasterCalcCrc(unsigned char *chData, unsigned short uNo)
{
    uint16_t crc = 0xffff;
    uint16_t i, j;
    for(i = 0; i < uNo; i++)
    {
        crc ^= chData[i];
        for(j = 0; j < 8; j++)
        {
            if(crc & 1)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    return (crc);
}
